////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2003-2006 Adobe Macromedia Software LLC and its licensors.
//  All Rights Reserved. The following is Source Code and is subject to all
//  restrictions on such code as contained in the End User License Agreement
//  accompanying this product.
//
////////////////////////////////////////////////////////////////////////////////

package mx.managers
{

import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Graphics;
import flash.display.InteractiveObject;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.utils.getTimer;
import mx.core.Application;
import mx.core.FlexSprite;
import mx.core.IChildList;
import mx.core.IFlexDisplayObject;
import mx.core.IInvalidating;
import mx.core.IUIComponent;
import mx.core.UIComponent;
import mx.core.mx_internal;
import mx.effects.Blur;
import mx.effects.Effect;
import mx.effects.Fade;
import mx.events.EffectEvent;
import mx.events.FlexEvent;
import mx.events.FlexMouseEvent;
import mx.managers.IFocusManagerContainer;
import mx.managers.PopUpManagerChildList;
import mx.styles.IStyleClient;

use namespace mx_internal;

/**
 *  The PopUpManager singleton class creates new top-level windows and
 *  places or removes those windows from the layer on top of all other
 *  visible windows.  See the SystemManager for a description of the layering.
 *  It is used for popup dialogs, menus, and dropdowns in the ComboBox control 
 *  and in similar components.
 * 
 *  <p>The PopUpManager also provides modality, so that windows below the popup
 *  cannot receive mouse events, and also provides an event if the user clicks
 *  the mouse outside the window so the developer can choose to dismiss
 *  the window or warn the user.</p>
 * 
 *  @see PopUpManagerChildList
 */
public class PopUpManager
{
	include "../core/Version.as";

	//--------------------------------------------------------------------------
	//
	//  Class assets
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 *  The class used to create the shield that makes a window appear modal.
	 */
	mx_internal static var modalWindowClass:Class;

	/**
	 *  @private
	 *  An array of information about currently active popups
	 */
	private static var popupInfo:Array;
	
	//--------------------------------------------------------------------------
	//
	//  Class methods
	//
	//--------------------------------------------------------------------------

	/**
	 *  Creates a top-level window and places it above other windows in the
	 *  z-order.
	 *  It is good practice to call the <code>removePopUp()</code> method 
	 *  to remove popups created by using the <code>createPopUp()</code> method.
	 *
	 *  If the class implements IFocusManagerContainer, the window will have its
	 *  own FocusManager so that, if the user uses the TAB key to navigate between
	 *  controls, only the controls in the window will be accessed.
	 *
	 *  <p><b>Example</b></p> 
	 *
	 *  <blockquote> 
	 *  <code> pop = mx.managers.PopUpManager.createPopUp(pnl, TitleWindow, false); </code>
	 *  </blockquote>
	 *  <p>Creates a popup window based on the TitleWindow class, using <code>pnl</code> as the MovieClip 
	 *  for determining where to place the popup. It is defined to be a non-modal window 
	 *  meaning that other windows can receive mouse events</p>
	 *
	 *  @param parent DisplayObject to be used for determining which SystemManager's layers
	 *  to use and optionally the reference point for centering the new
	 *  top level window.  It may not be the actual parent of the popup as all popups
	 *  are parented by the SystemManager.
	 * 
	 *  @param className Class of object that is to be created for the popup.
	 *  The class must implement IFlexDisplayObject.
	 *
	 *  @param modal If <code>true</code>, the window is modal which means that
	 *  the user will not be able to interact with other popups until the window
	 *  is removed.
	 *
	 *  @param childList The child list in which to add the popup.
	 *  One of <code>PopUpManagerChildList.APPLICATION</code>, 
	 *  <code>PopUpManagerChildList.POPUP</code>, 
	 *  or <code>PopUpManagerChildList.PARENT</code> (default).
	 *
	 *  @return Reference to new top-level window.
	 *
	 *  @see PopUpManagerChildList
	 */
	public static function createPopUp(parent:DisplayObject,
									   className:Class,
									   modal:Boolean = false,
									   childList:String = null):IFlexDisplayObject
	{	
		var window:UIComponent = new className();
		addPopUp(window, parent, modal, childList);
		return window;
	}
	
	/**
	 *  Pops up a top-level window.
	 *  It is good practice to call <code>removePopUp()</code> to remove popups
	 *  created by using the <code>createPopUp()</code> method.
	 *  If the class implements IFocusManagerContainer, the window will have its
	 *  own FocusManager so that, if the user uses the TAB key to navigate between
	 *  controls, only the controls in the window will be accessed.
	 *
	 *  <p><b>Example</b></p> 
	 *
	 *  <blockquote> 
	 *  <code> var tw = new TitleWindow();<br/>
	 *         tw.title = "My Title";<br/>
	 *         mx.managers.PopUpManager.addPopUp(tw, pnl, false);</code>
	 *  </blockquote>
	 *
	 *  <p>Creates a popup window using the <code>tw</code> instance of the 
	 *  TitleWindow class and <code>pnl</code> as the Sprite for determining
	 *  where to place the popup.
	 *  It is defined to be a non-modal window.</p>
	 *  
	 *  @param window The IFlexDisplayObject to be popped up.
	 *
	 *  @param parent DisplayObject to be used for determining which SystemManager's layers
	 *  to use and optionally  the reference point for centering the new
	 *  top level window.  It may not be the actual parent of the popup as all popups
	 *  are parented by the SystemManager.
	 *
	 *  @param modal If <code>true</code>, the window is modal which means that
	 *  the user will not be able to interact with other popups until the window
	 *  is removed.
	 *
	 *  @param childList The child list in which to add the pop-up.
	 *  One of <code>PopUpManagerChildList.APPLICATION</code>, 
	 *  <code>PopUpManagerChildList.POPUP</code>, 
	 *  or <code>PopUpManagerChildList.PARENT</code> (default).
	 *
	 *  @see PopUpManagerChildList
	 */	
	public static function addPopUp(window:IFlexDisplayObject,
									   parent:DisplayObject,
									   modal:Boolean = false,
									   childList:String = null):void
	{
		// trace("POPUP: window is " + window);
		// All popups go on the local root.
		// trace("POPUP: root is " + parent.root);
		// trace("POPUP: initial parent is " + parent);
		var localRoot:DisplayObjectContainer =
			DisplayObjectContainer(parent.root);
		var sm:ISystemManager;
		var visibleFlag:Boolean = window.visible;
		var topMost:Boolean;

		// If the parent isn't rooted yet,
		// use the global system manager instance.
		if (!localRoot && parent is IUIComponent)
		{
			localRoot =
				DisplayObjectContainer(IUIComponent(parent).systemManager);
		}
			
		var children:IChildList;
		if (localRoot is ISystemManager)
		{
			sm = ISystemManager(localRoot);
			if (!sm.isTopLevel())
				sm = sm.topLevelSystemManager;
		}
		else
		{
			//trace("error: popup root was not SystemManager");
			return; // and maybe a nice error message
		}
		
		if (window is UIComponent)
			UIComponent(window).isPopUp = true;
		
		if (!childList || childList == PopUpManagerChildList.PARENT)
		{
			topMost = sm.popUpChildren.contains(parent);
		}
		else
			topMost = (childList == PopUpManagerChildList.POPUP);
		
		children = topMost ? sm.popUpChildren : sm;
		children.addChild(DisplayObject(window));
		window.visible = false;
		
		if (!popupInfo)
			popupInfo = [];

		var o:PopUpData = new PopUpData();
		o.owner = DisplayObject(window);
		o.topMost = topMost;
		popupInfo.push(o);

		if (window is IFocusManagerContainer)
		{
			if (IFocusManagerContainer(window).focusManager)
				sm.addFocusManager(IFocusManagerContainer(window));
			else
				// Popups get their own focus loop
				IFocusManagerContainer(window).focusManager =
					new FocusManager(IFocusManagerContainer(window), true);
		}

		if (window is ILayoutManagerClient )
			UIComponent.layoutManager.validateClient(ILayoutManagerClient (window), true);
		
		o.parent = parent;
		
		if (window is DisplayObject)
			DisplayObject(window).cacheAsBitmap = true;
		
		if (window is IUIComponent)
		{
			IUIComponent(window).setActualSize(
				IUIComponent(window).getExplicitOrMeasuredWidth(),
				IUIComponent(window).getExplicitOrMeasuredHeight());
		}

		if (modal)
		{
			// create a modal window shield which blocks input and 
			// sets up mouseDownOutside logic
			PopUpManager.createModalWindow(parent, o, children, visibleFlag);
		}
		else
		{
			// if not modal, use this kind of mouseDownOutside logic
			o._mouseDownOutsideHandler = function(owner:DisplayObject, evt:MouseEvent):void
			{
   				// shapeFlag is false here for performance reasons
  				if (owner.hitTestPoint(evt.stageX, evt.stageY, true))
				{
					// do nothing
				}
				else
				{
					var event:MouseEvent =
						new FlexMouseEvent(FlexMouseEvent.MOUSE_DOWN_OUTSIDE);
					var pt:Point = new Point(evt.stageX, evt.stageY);
					pt = owner.globalToLocal(pt);
					event.localX = pt.x;
					event.localY = pt.y;
					event.buttonDown = evt.buttonDown;
					event.shiftKey = evt.shiftKey;
					event.altKey = evt.altKey;
					event.ctrlKey = evt.ctrlKey;
					event.delta = evt.delta;
					event.relatedObject = InteractiveObject(evt.target);
					owner.dispatchEvent(event);
				}
			}
			o._mouseWheelOutsideHandler = function(owner:DisplayObject, evt:MouseEvent):void
			{
   				// shapeFlag is false here for performance reasons
  				if (owner.hitTestPoint(evt.stageX, evt.stageY, true))
				{
					// do nothing
				}
				else
				{
					var event:MouseEvent =
						new FlexMouseEvent(FlexMouseEvent.MOUSE_WHEEL_OUTSIDE);
					var pt:Point = new Point(evt.stageX, evt.stageY);
					pt = owner.globalToLocal(pt);
					event.localX = pt.x;
					event.localY = pt.y;
					event.buttonDown = evt.buttonDown;
					event.shiftKey = evt.shiftKey;
					event.altKey = evt.altKey;
					event.ctrlKey = evt.ctrlKey;
					event.delta = evt.delta;
					event.relatedObject = InteractiveObject(evt.target);
					owner.dispatchEvent(event);
				}
			}

			sm.addEventListener(MouseEvent.MOUSE_DOWN, o.mouseDownOutsideHandler);
			sm.addEventListener(MouseEvent.MOUSE_WHEEL, o.mouseWheelOutsideHandler, true);
			
			window.visible = visibleFlag;
		}
			
		if (window is IFocusManagerContainer && visibleFlag)
			sm.activate(IFocusManagerContainer(window));

		// trace("END POPUP: addPopUp" + parent);
	}

    /**
     *  Centers a popup window over whatever window was used in the call 
     *  to the <code>createPopUp()</code> or <code>addPopUp()</code> method.
     *
     *  <p>Note that the position of the popup window may not
     *  change immediately after this call since Flex may wait to measure and layout the
     *  popup window before centering it.</p>
	 *
	 *  @param The IFlexDisplayObject representing the popup.
     */
	public static function centerPopUp(popUp:IFlexDisplayObject):void
	{
		if (popUp is IInvalidating)
			IInvalidating(popUp).validateNow();

		var n:int = popupInfo.length;
		for (var i:int = 0; i < n; i++)
		{
			var o:Object = popupInfo[i];
			if (o.owner == popUp && o.parent)
			{
				var pt:Point = new Point(0, 0);
				pt = o.parent.localToGlobal(pt);
				pt = popUp.parent.globalToLocal(pt);
				popUp.move(Math.round((o.parent.width - popUp.width) / 2) + pt.x,
						   Math.round((o.parent.height - popUp.height) / 2) + pt.y);
				break;
			}
		}
	}

	/**
	 *  Removes a popup window popped up by 
	 *  the <code>createPopUp()</code> or <code>addPopUp()</code> method.
	 *  
	 *  @param window The IFlexDisplayObject representing the popup window.
	 */
	public static function removePopUp(popUp:IFlexDisplayObject):void
	{
		if (popUp && popUp.parent)
		{
			if (popUp is UIComponent)
				UIComponent(popUp).isPopUp = false;
		
			var n:int = popupInfo.length;
			for (var i:int = 0; i < n; i++)
			{
				var o:PopUpData = popupInfo[i];
				if (o.owner == popUp)
				{
					var sm:ISystemManager = ISystemManager(popUp.parent);
					if (popUp is IFocusManagerContainer)
						sm.removeFocusManager(IFocusManagerContainer(popUp));
					// sometimes someone has found another way to unparent it
					// often in response to removeFocusManager
					if (DisplayObject(popUp).parent)
					{
						if (o.topMost)
							sm.popUpChildren.removeChild(DisplayObject(popUp));
						else
							sm.removeChild(DisplayObject(popUp));
						if (!o.modalWindow)
							popupInfo.splice(i, 1);
						sm.removeEventListener(MouseEvent.MOUSE_DOWN, o.mouseDownOutsideHandler);
						sm.removeEventListener(MouseEvent.MOUSE_WHEEL, o.mouseWheelOutsideHandler, true);
					}
					break;
				}
			}
		}
	}
	
	/**
	 *  Makes sure a popup window is higher than other objects in its child list
	 *  The SystemManager does this automatically if the popup is a top level window
	 *  and is moused on, 
	 *  but otherwise you have to take care of this yourself.
	 *
	 *  @param The IFlexDisplayObject representing the popup.
	 */
	public static function bringToFront(popUp:IFlexDisplayObject):void
	{
		if (popUp && popUp.parent)
		{
			var n:int = popupInfo.length;
			for (var i:int = 0; i < n; i++)
			{
				var o:PopUpData = popupInfo[i];
				if (o.owner == popUp)
				{
					var sm:ISystemManager = ISystemManager(popUp.parent);
					if (o.topMost)
						sm.popUpChildren.setChildIndex(DisplayObject(popUp), sm.popUpChildren.numChildren - 1);
					else
						sm.setChildIndex(DisplayObject(popUp), sm.numChildren - 1);
					break;
				}
			}
		}
	}
	
	/**
	 *  @private
	 *  Create the modal window.
	 */
	private static function createModalWindow(parentReference:DisplayObject,
											 o:PopUpData,
											 childrenList:IChildList,
											 visibleFlag:Boolean):void
	{
		var popup:IFlexDisplayObject = IFlexDisplayObject(o.owner);

		var popupStyleClient:IStyleClient = popup as IStyleClient;
		var duration:Number = 0;
		
		// Create a modalWindow the size of the stage
		// that eats all mouse clicks.
		var modalWindow:Sprite;
		if (modalWindowClass)
		{
			modalWindow = new modalWindowClass();
		}
		else
		{
			modalWindow = new FlexSprite();
			modalWindow.name = "modalWindow";
			
			var g:Graphics = modalWindow.graphics;
			var c:Number = 0xFFFFFF;
			
			if (popupStyleClient)
				c = popupStyleClient.getStyle("modalTransparencyColor");
			g.beginFill(c, 100);
			g.drawRect(0, 0, 1, 1);
			g.endFill();
		}
	
		var sm:ISystemManager = IUIComponent(parentReference).systemManager;
		sm.numModalWindows++;
	
		// Add it to the collection just below the popup
		childrenList.addChildAt(modalWindow,
			childrenList.getChildIndex(DisplayObject(popup)));

		// set alpha of the popup and get it out of the focus loop
		if (popupStyleClient)
			modalWindow.alpha = popupStyleClient.getStyle("modalTransparency");
		modalWindow.tabEnabled = false;
		var s:Rectangle = sm.screen;
		modalWindow.width = s.width;
		modalWindow.height = s.height;
		modalWindow.x = s.x;
		modalWindow.y = s.y;

		o.modalWindow = modalWindow;
		// a modal mousedownoutside handler just dispatches the event
		o._mouseDownOutsideHandler = function(owner:DisplayObject, evt:MouseEvent):void
		{
			var event:MouseEvent = new FlexMouseEvent(FlexMouseEvent.MOUSE_DOWN_OUTSIDE);
			var pt:Point = new Point(evt.stageX, evt.stageY);
			pt = owner.globalToLocal(pt);
			event.localX = pt.x;
			event.localY = pt.y;
			event.buttonDown = evt.buttonDown;
			event.shiftKey = evt.shiftKey;
			event.altKey = evt.altKey;
			event.ctrlKey = evt.ctrlKey;
			event.delta = evt.delta;
			event.relatedObject = InteractiveObject(evt.target);
			owner.dispatchEvent(event);
		}

		o._mouseWheelOutsideHandler = function(owner:DisplayObject, evt:MouseEvent):void
		{
			var event:MouseEvent = new FlexMouseEvent(FlexMouseEvent.MOUSE_WHEEL_OUTSIDE);
			var pt:Point = new Point(evt.stageX, evt.stageY);
			pt = owner.globalToLocal(pt);
			event.localX = pt.x;
			event.localY = pt.y;
			event.buttonDown = evt.buttonDown;
			event.shiftKey = evt.shiftKey;
			event.altKey = evt.altKey;
			event.ctrlKey = evt.ctrlKey;
			event.delta = evt.delta;
			event.relatedObject = InteractiveObject(evt.target);
			owner.dispatchEvent(event);
		}
		
		// Because it listens to the modal window
		modalWindow.addEventListener(MouseEvent.MOUSE_DOWN, o.mouseDownOutsideHandler);
		modalWindow.addEventListener(MouseEvent.MOUSE_WHEEL, o.mouseWheelOutsideHandler, true);
		// Set the resize handler so the modal can stay the size of the screen
		// use weak references
		sm.addEventListener(Event.RESIZE, resizeHandler, false, 0, true);

		// Listen for unload so we know to kill the modal window
		popup.addEventListener(Event.REMOVED, popupRemovedHandler, false, 0, true);
		
		// Listen for show so we know to show the modal window
		popup.addEventListener(FlexEvent.SHOW, popupShowHandler, false, 0, true);
		
		// Listen for hide so we know to hide the modal window
		popup.addEventListener(FlexEvent.HIDE, popupHideHandler, false, 0, true);
		
		if (visibleFlag)
			showModalWindow(o);
		else
			popup.visible = visibleFlag;
		
	}

    /**
     *  @private
     *  Set by PopUpManager on modal windows so they show when the parent shows
     */
    private static function popupShowHandler(event:FlexEvent):void
    {
		var n:int = popupInfo.length;
		for (var i:int = 0; i < n; i++)
		{
			var o:PopUpData = popupInfo[i];
			if (o.owner == event.target)
			{
				showModalWindow(o);
				break;
			}
		}
    }

    /**
     *  @private
     *  Set by PopUpManager on modal windows so they hide when the parent hide
     */
    private static function popupHideHandler(event:FlexEvent):void
    {
		var n:int = popupInfo.length;
		for (var i:int = 0; i < n; i++)
		{
			var o:PopUpData = popupInfo[i];
			if (o.owner == event.target)
			{
				hideModalWindow(o);
				break;
			}
		}
    }

	/**
	 *  @private
	 */
	private static function endEffects(o:PopUpData):void
	{
		if (o.fade)
		{
			o.fade.end();
			o.fade = null;
		}
		
		if (o.blur)
		{
			o.blur.end();
			o.blur = null;
		}
	}
	
	/**
	 *  @private
	 *  Show the modal transparency blocker, playing effects if needed.
	 */
	private static function showModalWindow(o:PopUpData):void
	{
		var popUpStyleClient:IStyleClient = o.owner as IStyleClient;
		var duration:Number = 0;
		
		if (popUpStyleClient)
			duration = popUpStyleClient.getStyle("modalTransparencyDuration");
		
		// End any effects that are currently playing for this popup.
		endEffects(o);
		
		if (duration)
		{
			// Fade effect on the modal transparency blocker
			var fade:Fade = new Fade(o.modalWindow);

			fade.alphaFrom = 0;
			fade.alphaTo = popUpStyleClient.getStyle("modalTransparency");
			fade.duration = duration;
			fade.addEventListener(EffectEvent.EFFECT_END, fadeInEffectEndHandler);

			o.modalWindow.alpha = 0;
			o.modalWindow.visible = true;
			o.fade = fade;
			IUIComponent(o.owner).setVisible(false, true);
			
			fade.play();
			
			// Blur effect on the application
			var blurAmount:Number = popUpStyleClient.getStyle("modalTransparencyBlur");
			
			if (blurAmount)
			{
				var blur:Blur = new Blur(Application.application);

				blur.blurXFrom = blur.blurYFrom = 0;
				blur.blurXTo = blur.blurYTo = blurAmount;
				blur.duration = duration;
				blur.addEventListener(EffectEvent.EFFECT_END, effectEndHandler);
				
				o.blur = blur;
				
				blur.play();
			}
		}
		else
		{
			IUIComponent(o.owner).setVisible(true, true);
			o.modalWindow.visible = true;
		}
	}
	
	/**
	 *  @private
	 *  Hide the modal transparency blocker, playing effects if needed.
	 */
	private static function hideModalWindow(o:PopUpData, destroy:Boolean = false):void
	{
		var popUpStyleClient:IStyleClient = o.owner as IStyleClient;
		var duration:Number = 0;
		
		if (popUpStyleClient)
			duration = popUpStyleClient.getStyle("modalTransparencyDuration");
		
		// end any effects that are current playing for this popup
		endEffects(o);
		
		if (duration)
		{
			// Fade effect on the modal transparency blocker
			var fade:Fade = new Fade(o.modalWindow);

			fade.alphaFrom = o.modalWindow.alpha;
			fade.alphaTo = 0;
			fade.duration = duration;
			fade.addEventListener(EffectEvent.EFFECT_END, 
				destroy ? fadeOutDestroyEffectEndHandler : fadeOutCloseEffectEndHandler);

			o.modalWindow.visible = true;
			o.fade = fade;
			fade.play();
			
			// Blur effect on the application
			var blurAmount:Number = popUpStyleClient.getStyle("modalTransparencyBlur");
			
			if (blurAmount)
			{
				var blur:Blur = new Blur(Application.application);

				blur.blurXFrom = blur.blurYFrom = blurAmount;
				blur.blurXTo = blur.blurYTo = 0;
				blur.duration = duration;
				blur.addEventListener(EffectEvent.EFFECT_END, effectEndHandler);
				o.blur = blur;
				
				blur.play();
			}
		}
		else
		{
			o.modalWindow.visible = false;
		}
	}
	
 	//--------------------------------------------------------------------------
	//
	//  Class event handlers
	//
	//--------------------------------------------------------------------------

    /**
     *  @private
     *  Set by PopUpManager on modal windows to make sure they cover the whole screen
     */
    private static function resizeHandler(event:Event):void
    {
        var s:Rectangle = ISystemManager(DisplayObject(event.target).root).screen;

		var n:int = popupInfo.length;
		for (var i:int = 0; i < n; i++)
		{
			var o:PopUpData = popupInfo[i];
			if (o.modalWindow && o.owner.stage == DisplayObject(event.target).stage)
			{
				o.modalWindow.width = s.width;
				o.modalWindow.height = s.height;
				o.modalWindow.x = s.x;
				o.modalWindow.y = s.y;
			}
		}
    }

    /**
     *  @private
     *  Set by PopUpManager on modal windows to monitor when the parent window gets killed
     */
    private static function popupRemovedHandler(event:Event):void
    {
		var n:int = popupInfo.length;
		for (var i:int = 0; i < n; i++)
		{
			var o:PopUpData = popupInfo[i];
			if (o.owner == event.target)
			{
				var sm:ISystemManager = o.parent is ISystemManager ?
										ISystemManager(o.parent) :
										IUIComponent(o.parent).systemManager;

				hideModalWindow(o, true);
				sm.numModalWindows--;				
				popupInfo.splice(i, 1);
				break;
			}
		}
    }
    
    /**
     *  @private
     *  Show the modal window after the fade effect finishes
     */
	private static function fadeInEffectEndHandler(event:EffectEvent):void
	{
		effectEndHandler(event);
		
		var n:int = popupInfo.length;
		for (var i:int = 0; i < n; i++)
		{
			var o:PopUpData = popupInfo[i];
			if (o.modalWindow == event.effectInstance.target)
			{
				IUIComponent(o.owner).setVisible(true, true);
				break;
			}
		}
	}
    
    /**
     *  @private
     *  Remove the modal window after the fade effect finishes
     */
	private static function fadeOutDestroyEffectEndHandler(event:EffectEvent):void
	{
		effectEndHandler(event);
		var obj:DisplayObject = DisplayObject(event.effectInstance.target);
		if (obj.parent is ISystemManager)
		{
			var sm:ISystemManager = ISystemManager(obj.parent)
			if (sm.popUpChildren.contains(obj))
				sm.popUpChildren.removeChild(obj);
			else
				sm.removeChild(obj);
		}
		else
			obj.parent.removeChild(obj);
	}
    
    /**
     *  @private
     *  Remove the modal window after the fade effect finishes
     */
	private static function fadeOutCloseEffectEndHandler(event:EffectEvent):void
	{
		effectEndHandler(event);
		var obj:DisplayObject = DisplayObject(event.effectInstance.target);
		obj.visible = false;
	}
	
	/**
	 *  @private
	 */
	private static function effectEndHandler(event:EffectEvent):void
	{
		var n:int = popupInfo.length;
		for (var i:int = 0; i < n; i++)
		{
			var o:PopUpData = popupInfo[i];
			var e:Effect = event.effectInstance.effect;
			
			if (e == o.fade)
				o.fade = null;
			else if (e == o.blur)
				o.blur = null;
		}
	}
	
}

}

////////////////////////////////////////////////////////////////////////////////
//
//  Helper class: PopUpData
//
////////////////////////////////////////////////////////////////////////////////


import flash.display.DisplayObject;
import flash.events.MouseEvent;
import mx.effects.Effect;

/**
 *  @private
 */
class PopUpData
{
	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------

	/**
	 *  Constructor.
	 */
	public function PopUpData()
	{
		super();
	}
	
	//--------------------------------------------------------------------------
	//
	//  Variables
	//
	//--------------------------------------------------------------------------
	
	/**
	 *  @private
	 */
    public var owner:DisplayObject;

	/**
	 *  @private
	 */
    public var parent:DisplayObject;

	/**
	 *  @private
	 */
    public var topMost:Boolean;

	/**
	 *  @private
	 */
    public var modalWindow:DisplayObject;

	/**
	 *  @private
	 */
	public var _mouseDownOutsideHandler:Function;

	/**
	 *  @private
	 */
	public var _mouseWheelOutsideHandler:Function;

	/**
	 *  @private
	 */
	public var fade:Effect;

	/**
	 *  @private
	 */
	public var blur:Effect;
	
	//--------------------------------------------------------------------------
	//
	//  Event handlers
	//
	//--------------------------------------------------------------------------

	/**
	 *  @private
	 */
	public function mouseDownOutsideHandler(event:MouseEvent):void
	{
		_mouseDownOutsideHandler(owner, event);
	}

	/**
	 *  @private
	 */
	public function mouseWheelOutsideHandler(event:MouseEvent):void
	{
		_mouseWheelOutsideHandler(owner, event);
	}
}
